package com.chaodev.base.controller;

import cn.afterturn.easypoi.csv.CsvImportUtil;
import cn.afterturn.easypoi.csv.entity.CsvImportParams;
import cn.afterturn.easypoi.excel.ExcelExportUtil;
import cn.afterturn.easypoi.excel.ExcelImportUtil;
import cn.afterturn.easypoi.excel.entity.ImportParams;
import cn.afterturn.easypoi.excel.entity.TemplateExportParams;
import cn.afterturn.easypoi.excel.entity.result.ExcelImportResult;
import cn.afterturn.easypoi.util.UnicodeInputStream;
import com.github.pagehelper.PageInfo;
import com.google.common.collect.Lists;
import com.chaodev.base.model.*;
import com.chaodev.base.base.RedisDao;
import com.chaodev.base.service.ILogService;
import com.chaodev.base.service.UploadDownloadService;
import com.chaodev.base.utils.FileUtils;
import com.chaodev.base.utils.JwtUtil;
import io.swagger.annotations.ApiOperation;
import org.apache.poi.ss.usermodel.Workbook;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.validation.ConstraintViolation;
import javax.validation.Validation;
import javax.validation.Validator;
import javax.validation.ValidatorFactory;
import java.io.*;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.regex.Pattern;

/**
 * @author lccccc
 */
@RestController
@RequestMapping(value = "/file")
public class FileController {
    @Autowired
    private UploadDownloadService uploadDownloadService;

    @Autowired
    RedisDao redisDao;

    @Autowired
    ILogService logService;

    @Autowired
    UploadController controller;

    @Value("${variables.importSaveType}")
    private String importSaveType;

    @Value("${variables.importSavePath}")
    private String importSavePath;

    private final Pattern pattern = Pattern.compile("[\u4e00-\u9fa5]");

    @RequestMapping(value = "/upload", method = RequestMethod.POST)
    @ResponseBody
    @ApiOperation(value = "导入")
    @Transactional(rollbackFor = Exception.class)
    public Result upload(@RequestParam("file") MultipartFile[] file,
                         // separator默认为匹配一个或多个任意空字符(空格和Tab键)
                         @RequestParam(value = "separator", required = false, defaultValue = "\\s+") String separator,
                         HttpServletRequest request, HttpServletResponse response) {
        Result result = new Result();
        String token = request.getHeader("token");
        UserInfo userInfo = new UserInfo();
        try {
            if (redisDao.existsKey(token)) {
                // 用于记时
                long startTime = System.currentTimeMillis();
                userInfo = JwtUtil.getUserInfoFromToken(token, redisDao);
                String[] strings = file[0].getOriginalFilename().split("\\.");
                SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
                String date = sdf.format(System.currentTimeMillis());
                // 生成文件标识（使用uuid）
                String uuid = UUID.randomUUID().toString().replace("-", "");
                if ("csv".equals(strings[strings.length - 1])) {
                    // 处理csv文件(csv编码若为utf8则必须含有bom，否则保存后的原文件会乱码)
                    CsvImportParams params = new CsvImportParams(CsvImportParams.UTF8);
                    // csv无法返回错误的行（或参考txt的解析方式实现），只能返回正确的行，目前没有处理校验结果
                    params.setNeedVerify(true);
                    List<Student> studentList1 = CsvImportUtil.importCsv(file[0].getInputStream(), Student.class, params);
                    for (Student student : studentList1) {
                        student.setId(uuid);
                        student.setCreateUser(userInfo.getUserId());
                        student.setCreateTime(date);
                    }
                    // 去重，使用了set，不保证顺序
                    List<Student> distinctList = distinct(studentList1, 1);
                    // 原文件保存没有处理是否保存成功
                    saveFile(file, userInfo, uuid, strings[strings.length - 1]);
                    // 保存到数据库(临时表)
                    // 每个线程导入的条数
                    int count = 1000;
                    int listSize = distinctList.size();
                    int runSize = (listSize / count) + 1;
                    List<Student> newlist;
                    Integer resRows = 0;
                    // 线程的返回结果
                    CompletableFuture<Integer> completableFuture = null;
                    for (int j = 0; j < runSize; j++) {
                        int startIndex = (j * count);
                        int endIndex;
                        if (j + 1 == runSize) {
                            endIndex = listSize;
                        } else {
                            endIndex = (j + 1) * count;
                        }
                        newlist = distinctList.subList(startIndex, endIndex);
                        completableFuture = uploadDownloadService.uploadThread(newlist);
                        resRows += completableFuture.get();
                    }
                    if (resRows == listSize) {
                        result.setMsg("解析校验成功！");
                        // 返回批次号，用于预览
                        result.setData(uuid);
                        result.setCode(ResultEnum.SUCCESS.getCode());
                    } else {
                        result.setMsg("解析校验失败！");
                        result.setCode(ResultEnum.NODATE.getCode());
                    }
                    long endTime = System.currentTimeMillis();
                    long usedTime = (endTime - startTime) / 1000;
                    String res = "CsvOriginalRows:" + studentList1.size() + " ImportRows:" + distinctList.size() + " time:" + usedTime + "s";
                    logService.logAdd(res, "解析校验文件并导入临时库", "SUCCESS", result.toString(), userInfo, request);
                } else if ("txt".equals(strings[strings.length - 1])) {
                    //txt文件处理
                    String encoding = "UTF-8";
                    // 去除bom
                    byte[] head = new byte[3];
                    InputStream inputStream = file[0].getInputStream();
                    inputStream = new PushbackInputStream(inputStream, 3);
                    inputStream.read(head);
                    if (head[0] == -17 && head[1] == -69 && head[2] == -65) {
                        ((PushbackInputStream) inputStream).unread(head, 0, 3);
                        inputStream = new UnicodeInputStream(inputStream);
                    }
                    BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream, encoding));
                    String line = null;
                    List<Student> list = new ArrayList<>();
                    List<Student> failList = new ArrayList<>();
                    boolean verify = true;
                    int i = 0;
                    // 使用hibernate-validator校验，在实体类上配置注解。
                    ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
                    Validator validator = factory.getValidator();
                    // 读取txt文件的每一行，再根据分隔符分割成字符串数组，再赋值给对象list
                    while ((line = bufferedReader.readLine()) != null) {
                        verify = true;
                        // 默认：根据一个或多个空格或Tab分割每一行的字符串（匹配一次或多次空白字符）
                        String[] arr = line.split(separator);
                        // 赋值与校验
                        Student student = new Student();
                        student.setId(uuid);
                        student.setCreateUser(userInfo.getUserId());
                        student.setCreateTime(date);
                        student.setName(arr[0]);
                        student.setClasses(arr[1]);
                        student.setSex(arr[2]);
                        student.setScore(Double.valueOf(arr[3]));
                        student.setArea(arr[4]);
                        student.setCode(arr[5]);
                        student.setPhone(arr[6]);
                        StringBuilder errMsg = null;
                        Set<ConstraintViolation<Student>> violations = validator.validate(student);
                        for (ConstraintViolation violation : violations) {
                            if (errMsg == null || errMsg.length() == 0) {
                                errMsg = new StringBuilder(violation.getMessage());
                            } else {
                                errMsg.append(",").append(violation.getMessage());
                            }
                            verify = false;
                        }
                        violations.clear();
                        if (!verify) {
                            student.setRowNum(i);
                            student.setErrorMsg(errMsg.toString());
                            failList.add(student);
                        }
                        list.add(student);
                        i++;
                    }
                    // 原文件保存没有处理是否保存成功
                    saveFile(file, userInfo, uuid, strings[strings.length - 1]);
                    if (failList.size() > 0) {
                        // 校验失败
                        result.setData(failList);
                        result.setCode(ResultEnum.NODATE.getCode());
                        result.setMsg("数据校验失败！");
                        long endTime = System.currentTimeMillis();
                        long usedTime = (endTime - startTime) / 1000;
                        String res = "TxtOriginalRows:" + list.size() + " time:" + usedTime + "s";
                        logService.logAdd(res, "解析校验文件并导入临时库", "SUCCESS", result.toString(), userInfo, request);
                    } else {
                        // 校验成功，保存到数据库(临时表)
                        // 去重，使用了set，不保证顺序
                        List<Student> distinctList = distinct(list, 1);
                        // 每个线程导入的条数
                        int count = 1000;
                        int listSize = distinctList.size();
                        int runSize = (listSize / count) + 1;
                        List<Student> newlist;
                        Integer resRows = 0;
                        // 线程的返回结果
                        CompletableFuture<Integer> completableFuture = null;
                        for (int j = 0; j < runSize; j++) {
                            int startIndex = (j * count);
                            int endIndex;
                            if (j + 1 == runSize) {
                                endIndex = listSize;
                            } else {
                                endIndex = (j + 1) * count;
                            }
                            newlist = distinctList.subList(startIndex, endIndex);
                            completableFuture = uploadDownloadService.uploadThread(newlist);
                            resRows += completableFuture.get();
                        }
                        if (resRows == listSize) {
                            result.setMsg("解析校验成功！");
                            // 返回批次号，用于预览
                            result.setData(uuid);
                            result.setCode(ResultEnum.SUCCESS.getCode());
                        } else {
                            result.setMsg("解析校验失败！");
                            result.setCode(ResultEnum.NODATE.getCode());
                        }
                        long endTime = System.currentTimeMillis();
                        long usedTime = (endTime - startTime) / 1000;
                        String res = "TxtOriginalRows:" + list.size() + " ImportRows:" + distinctList.size() + " time:" + usedTime + "s";
                        logService.logAdd(res, "解析校验文件并导入临时库", "SUCCESS", result.toString(), userInfo, request);
                    }
                } else {
                    ImportParams importParams = new ImportParams();
                    //表头行数,默认1
                    importParams.setHeadRows(1);
                    //表格标题行数,默认0
                    importParams.setTitleRows(0);
                    // 开启数据校验
                    importParams.setNeedVerify(true);
                    // 开启多线程导入
                    // importParams.setConcurrentTask(true);
                    ExcelImportResult<Student> fileResult1 = ExcelImportUtil.importExcelMore(file[0].getInputStream(), Student.class, importParams);
                    if (fileResult1.isVerfiyFail()) {
                        // 校验失败
                        result.setData(fileResult1.getFailList());
                        result.setCode(ResultEnum.NODATE.getCode());
                        result.setMsg("数据校验失败！");
                        long endTime = System.currentTimeMillis();
                        long usedTime = (endTime - startTime) / 1000;
                        String res = "XlsOrXlsxOriginalRows:" + fileResult1.getList().size() + " time:" + usedTime + "s";
                        logService.logAdd(res, "解析校验文件并导入临时库", "SUCCESS", result.toString(), userInfo, request);
                    } else {
                        // 通过校验
                        List<Student> fileResultListList1 = fileResult1.getList();
                        for (Student student : fileResultListList1) {
                            student.setId(uuid);
                            student.setCreateUser(userInfo.getUserId());
                            student.setCreateTime(date);
                        }
                        // 去重，使用了set，不保证顺序
                        List<Student> distinctList = distinct(fileResultListList1, 1);
                        // 原文件保存没有处理是否保存成功
                        saveFile(file, userInfo, uuid, strings[strings.length - 1]);
                        // 校验成功，保存到数据库(临时表)
                        // 每个线程导入的条数
                        int count = 1000;
                        int listSize = distinctList.size();
                        int runSize = (listSize / count) + 1;
                        List<Student> newlist;
                        Integer resRows = 0;
                        // 线程的返回结果
                        CompletableFuture<Integer> completableFuture = null;
                        for (int j = 0; j < runSize; j++) {
                            int startIndex = (j * count);
                            int endIndex;
                            if (j + 1 == runSize) {
                                endIndex = listSize;
                            } else {
                                endIndex = (j + 1) * count;
                            }
                            newlist = distinctList.subList(startIndex, endIndex);
                            completableFuture = uploadDownloadService.uploadThread(newlist);
                            resRows += completableFuture.get();
                        }
                        if (resRows == listSize) {
                            result.setMsg("解析校验成功！");
                            // 返回批次号，用于预览
                            result.setData(uuid);
                            result.setCode(ResultEnum.SUCCESS.getCode());
                        } else {
                            result.setMsg("解析校验失败！");
                            result.setCode(ResultEnum.NODATE.getCode());
                        }
                        long endTime = System.currentTimeMillis();
                        long usedTime = (endTime - startTime) / 1000;
                        String res = "XlsOrXlsxOriginalRows:" + fileResultListList1.size() + " ImportRows:" + distinctList.size() + " time:" + usedTime + "s";
                        logService.logAdd(res, "解析校验文件并导入临时库", "SUCCESS", result.toString(), userInfo, request);
                    }
                }
            } else {
                result.setCode(-1);
                result.setMsg("登录超时");
                logService.logAdd(null, "解析校验文件并导入临时库", "ERROR", result.toString(), userInfo, request);
            }
        } catch (Exception e) {
            result.setCode(ResultEnum.NODATE.getCode());
            result.setMsg("文件解析错误，请检查文件中数据有效且编码为UTF-8！");
            logService.logAdd(null, "解析校验文件并导入临时库", "ERROR", result.toString(), userInfo, request);
            return result;
        }
        return result;
    }

    public boolean saveFile(MultipartFile[] file, UserInfo userInfo, String uuid, String type) {
        boolean flag = false;
        try {
            if ("file".equals(importSaveType)) {
                flag = controller.folderUpload1(file, userInfo, uuid);
            } else if ("ftp".equals(importSaveType)) {
                flag = controller.ftpUpload1(file, userInfo, uuid);
            } else {
                flag = controller.upload(file[0], uuid, importSavePath);
            }
        } catch (Exception e) {
            System.out.println("e.getMessage() = " + e.getMessage());
        }
        return flag;
    }

    public List<Student> distinct(List<Student> list, int flag) {
        // 经测试1000000数据三种方法用时都小于一秒。
        if (flag == 1) {
            // 方法一：遍历使用set去重，需要重写实体类的equals和hashcode方法,根据相关属性判断是否重复
            // repeatList为重复的数据
            List<Student> repeatList = new ArrayList<>();
            // rowNum为错误的行号
            int row = 0;
            List<Integer> rowNum = new ArrayList<>();
            // set：没有重复的数据（不保证顺序）
            HashSet<Student> set = new HashSet<>();
            for (Student student : list) {
                boolean add = set.add(student);
                if (!add) {
                    repeatList.add(student);
                    rowNum.add(row);
                }
                row++;
            }
            return new ArrayList<>(set);
        } else if (flag == 2) {
            // 方法二：直接转set去重，需要重写实体类的equals和hashcode方法,根据相关属性判断是否重复
            // studentHashSet：没有重复的数据（不保证顺序）
            Set<Student> studentHashSet = new HashSet<>(list);
            if (list.size() == studentHashSet.size()) {
                System.out.println("没有重复元素");
            } else {
                System.out.println("有重复元素");
            }
        } else {
            // 方法三：使用Stream判断，只能知道是否重复
            long count = list.stream().distinct().count();
            if (list.size() == count) {
                System.out.println("没有重复元素");
            } else {
                System.out.println("有重复元素");
            }
        }
        return new ArrayList<>();
    }

    @GetMapping("/preview")
    @ApiOperation(value = "预览")
    public Result preview(@RequestParam(value = "uuId") String uuid, HttpServletRequest request, HttpServletResponse response) {
        Result result = new Result();
        String token = request.getHeader("token");
        UserInfo userInfo = new UserInfo();
        try {
            if (redisDao.existsKey(token)) {
                userInfo = JwtUtil.getUserInfoFromToken(token, redisDao);
                // 从临时表中查询，进行预览，仅仅返回前20行
                List<Student> studentList = uploadDownloadService.findByIdTemp(uuid);
                result.setCode(ResultEnum.SUCCESS.getCode());
                result.setData(studentList);
                Result logResult = new Result();
                logResult.setCode(0);
                //不记录返回数据，只做标识
                logResult.setMsg("数据获取成功");
                logResult.setServerID(result.getServerID());
                logService.logAdd(uuid, "预览", "SUCCESS", logResult.toString(), userInfo, request);
            } else {
                result.setCode(-1);
                result.setMsg("登录超时");
                logService.logAdd(uuid, "预览", "ERROR", result.toString(), userInfo, request);
            }
        } catch (Exception e) {
            result.setCode(ResultEnum.NODATE.getCode());
            result.setMsg("查询错误！");
            logService.logAdd(uuid, "预览", "ERROR", result.toString(), userInfo, request);
            return result;
        }
        return result;
    }

    @PostMapping("/check")
    @ApiOperation(value = "查看提交的数据")
    public Result check(@RequestBody DataPageVO dataPage, HttpServletRequest request, HttpServletResponse response) {
        Result result = new Result();
        String token = request.getHeader("token");
        UserInfo userInfo = new UserInfo();
        try {
            if (redisDao.existsKey(token)) {
                userInfo = JwtUtil.getUserInfoFromToken(token, redisDao);
                // 根据批次号查看数据
                Map<String, Object> map = new HashMap<>();
                map.put("P_PAGE", dataPage.getPage());
                map.put("P_PAGESIZE", dataPage.getRows());
                map.put("ID", dataPage.getKeyword());
                PageInfo<Student> studentPageInfo = uploadDownloadService.findById(map);
                result.setCode(ResultEnum.SUCCESS.getCode());
                result.setData(studentPageInfo);
                Map<String, Object> reMap = new HashMap<>();
                reMap.put("total", studentPageInfo.getTotal());
                reMap.put("rows", studentPageInfo.getList());
                result.setCode(ResultEnum.SUCCESS.getCode());
                result.setData(reMap);
                Result logResult = new Result();
                logResult.setCode(0);
                //不记录返回数据，只做标识
                logResult.setMsg("数据获取成功");
                logResult.setServerID(result.getServerID());
                logService.logAdd(dataPage.toString(), "查看提交的数据", "SUCCESS", logResult.toString(), userInfo, request);
            } else {
                result.setCode(-1);
                result.setMsg("登录超时");
                logService.logAdd(dataPage.toString(), "查看提交的数据", "ERROR", result.toString(), userInfo, request);
            }
        } catch (Exception e) {
            result.setCode(ResultEnum.NODATE.getCode());
            result.setMsg("查询错误！");
            logService.logAdd(dataPage.toString(), "查看提交的数据", "ERROR", result.toString(), userInfo, request);
            return result;
        }
        return result;
    }

    @PostMapping("/submit")
    @ApiOperation(value = "提交数据")
    @Transactional(rollbackFor = Exception.class)
    public Result submit(@RequestBody FileInfo fileInfo, HttpServletRequest request, HttpServletResponse response) {
        Result result = new Result();
        String token = request.getHeader("token");
        UserInfo userInfo = new UserInfo();
        try {
            if (redisDao.existsKey(token)) {
                userInfo = JwtUtil.getUserInfoFromToken(token, redisDao);
                fileInfo.setCreateUser("admin");
                SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
                SimpleDateFormat sdf1 = new SimpleDateFormat("yyyyMMdd");
                String date = sdf.format(System.currentTimeMillis());
                String date1 = sdf1.format(System.currentTimeMillis());
                String[] split = fileInfo.getFileName().split("\\.");
                fileInfo.setCreateDate(date);
                fileInfo.setFilePath(date1 + "/" + fileInfo.getId() + "." + split[split.length - 1]);
                // 文件大小转换为kb
                fileInfo.setFileSize(String.valueOf((int)(Math.ceil((Double.parseDouble(fileInfo.getFileSize()) / 1024)))));
                // 插入文件相关信息，创建人，创建时间，备注等
                boolean flag1 = uploadDownloadService.insetInfo(fileInfo);
                // 将数据从临时表插入正式表表插入
                boolean flag2 = uploadDownloadService.insertData(fileInfo.getId());
                Result logResult = new Result();
                if (flag1 && flag2) {
                    result.setCode(ResultEnum.SUCCESS.getCode());
                    result.setMsg("成功！");
                    logResult.setCode(0);
                    //不记录返回数据，只做标识
                    logResult.setMsg("导入成功！");
                    logResult.setServerID(result.getServerID());
                    logService.logAdd(fileInfo.toString(), "提交导入的文件详情与插入正式表", "SUCCESS", logResult.toString(), userInfo, request);
                } else {
                    result.setCode(ResultEnum.NODATE.getCode());
                    result.setMsg("失败！");
                    logResult.setCode(0);
                    //不记录返回数据，只做标识
                    logResult.setMsg("导入失败！");
                    logResult.setServerID(result.getServerID());
                    logService.logAdd(fileInfo.toString(), "提交导入的文件详情与插入正式表", "ERROR", logResult.toString(), userInfo, request);
                }
            } else {
                result.setCode(-1);
                result.setMsg("登录超时");
                logService.logAdd(fileInfo.toString(), "提交导入的文件详情与插入正式表", "ERROR", result.toString(), userInfo, request);
            }
        } catch (Exception e) {
            result.setCode(ResultEnum.NODATE.getCode());
            result.setMsg("插入错误！" + e.getMessage());
            logService.logAdd(fileInfo.toString(), "提交导入的文件详情与插入正式表", "ERROR", result.toString(), userInfo, request);
            return result;
        }
        return result;
    }

    @PostMapping("/getAllFileInfo")
    @ApiOperation(value = "获取提交的文件信息")
    public Result getAllFileInfo(@RequestBody DataPageVO dataPage, HttpServletRequest request, HttpServletResponse response) {
        Result result = new Result();
        String token = request.getHeader("token");
        UserInfo userInfo = new UserInfo();
        try {
            if (redisDao.existsKey(token)) {
                userInfo = JwtUtil.getUserInfoFromToken(token, redisDao);
                dataPage.setUserId(userInfo.getUserId());
                // 从正式表获取该用户提交的所有数据
                Map<String, Object> map = new HashMap<>();
                map.put("P_PAGE", dataPage.getPage());
                map.put("P_PAGESIZE", dataPage.getRows());
                map.put("USERID", dataPage.getUserId());
                PageInfo<FileInfo> fileInfoPageInfo = uploadDownloadService.getAllFileInfo(map);
                Map<String, Object> reMap = new HashMap<>();
                reMap.put("total", fileInfoPageInfo.getTotal());
                reMap.put("rows", fileInfoPageInfo.getList());
                result.setCode(ResultEnum.SUCCESS.getCode());
                result.setData(reMap);
                Result logResult = new Result();
                logResult.setCode(0);
                //不记录返回数据，只做标识
                logResult.setMsg("数据获取成功");
                logResult.setServerID(result.getServerID());
                logService.logAdd(dataPage.toString(), "获取所有提交的文件信息", "SUCCESS", logResult.toString(), userInfo, request);
            } else {
                result.setCode(-1);
                result.setMsg("登录超时");
                logService.logAdd(dataPage.toString(), "获取所有提交的文件信息", "ERROR", result.toString(), userInfo, request);
            }
        } catch (Exception e) {
            System.out.println("e = " + e.getMessage().toString());
            result.setCode(ResultEnum.NODATE.getCode());
            result.setMsg("插入错误！");
            return result;
        }
        return result;
    }

    @GetMapping("/exportExampleFile")
    @ApiOperation(value = "导出模板文件")
    public Result exportExampleFile(@RequestParam(value = "type") String type,
                                    @RequestParam(value = "token") String token,
                                    HttpServletRequest request, HttpServletResponse response) {
        Result result = new Result();
        Result logResult = new Result();
        UserInfo userInfo = new UserInfo();
        try {
            if (redisDao.existsKey(token)) {
                userInfo = JwtUtil.getUserInfoFromToken(token, redisDao);
                // 示例行
                List<Student> studentList = new ArrayList<>();
                Student student = new Student();
                student.setName("李明");
                student.setClasses("一班");
                student.setSex("男");
                student.setScore(90.0);
                student.setArea("四川省成都市金牛区西南交通大学九里校区");
                student.setPhone("18000000000");
                student.setCode("56000000");
                studentList.add(student);
                if ("xls".equals(type) || "csv".equals(type)) {
                    logResult.setCode(0);
                    //不记录返回数据，只做标识
                    logResult.setMsg(type + "导出模板文件");
                    logResult.setServerID(result.getServerID());
                    logService.logAdd(type, "导出模板文件", "SUCCESS", logResult.toString(), userInfo, request);
                    FileUtils.exportExcel(studentList, null, "Sheet1", Student.class, "example" + "." + "xls", true, response);
                } else if ("xlsx".equals(type)) {
                    logResult.setCode(0);
                    //不记录返回数据，只做标识
                    logResult.setMsg(type + "导出模板文件");
                    logResult.setServerID(result.getServerID());
                    logService.logAdd(type, "导出模板文件", "SUCCESS", logResult.toString(), userInfo, request);
                    FileUtils.exportExcel(studentList, null, "Sheet1", Student.class, "example" + "." + "xlsx", true, response);
                } else if ("txt".equals(type)) {
                    logResult.setCode(0);
                    //不记录返回数据，只做标识
                    logResult.setMsg(type + "导出模板文件");
                    logResult.setServerID(result.getServerID());
                    logService.logAdd(type, "导出模板文件", "SUCCESS", logResult.toString(), userInfo, request);
                    // 需要重写实体类的toString方法
                    String str = student.toString();
                    FileUtils.exportTxt(response, str, "example" + "." + "txt");
                }
            } else {
                result.setCode(-1);
                result.setMsg("登录超时");
                logService.logAdd(type, "导出模板文件", "ERROR", result.toString(), userInfo, request);
            }
        } catch (Exception e) {
            System.out.println("e.getMessage().toString() = " + e.getMessage().toString());
            result.setCode(ResultEnum.NODATE.getCode());
            result.setMsg("导出错误！");
            logService.logAdd(type, "导出模板文件", "ERROR", result.toString(), userInfo, request);
            return result;
        }
        return result;
    }

    @GetMapping("/export")
    @ApiOperation(value = "导出")
    public Result export(@RequestParam(value = "uuId") String uuid,
                         @RequestParam(value = "token") String token,
                         HttpServletRequest request, HttpServletResponse response) {
        Result result = new Result();
        UserInfo userInfo = new UserInfo();
        try {
            if (redisDao.existsKey(token)) {
                Result logResult = new Result();
                userInfo = JwtUtil.getUserInfoFromToken(token, redisDao);
                // 导出参数，因为不好测试，所以对象写在这里
                FileParam fileParam = new FileParam();
                if (null == fileParam.getFileName() || "".equals(fileParam.getFileName())) {
                    SimpleDateFormat sdf1 = new SimpleDateFormat("yyyyMMdd");
                    String date1 = sdf1.format(System.currentTimeMillis());
                    fileParam.setFileName("导出数据_" + date1);
                }
                if (null == fileParam.getSuffix() || "".equals(fileParam.getSuffix())) {
                    // xls,xlsx
                    fileParam.setSuffix("xlsx");
                }
                if (null == fileParam.getSheetName() || "".equals(fileParam.getSheetName())) {
                    fileParam.setSheetName("Sheet1");
                }
                if (null == fileParam.getTitle() || "".equals(fileParam.getTitle())) {
                    fileParam.setTitle(null);
                }
                // 查询数据量
                int num = uploadDownloadService.getNnm(uuid);
                // 数据量大于65000，强制使用xlsx格式，大于10万需要联系管理员导出
                if (num > 65000 && num < 100000) {
                    // 读数据
                    List<Student> studentList = uploadDownloadService.findAll(uuid);
                    fileParam.setSuffix("xlsx");
                    logResult.setCode(0);
                    //不记录返回数据，只做标识
                    logResult.setMsg(fileParam.getSuffix() + " ExportRows:" + num);
                    logResult.setServerID(result.getServerID());
                    logService.logAdd("批次号:" + uuid, "excel导出", "SUCCESS", logResult.toString(), userInfo, request);
                    // 导出
                    FileUtils.exportExcel(studentList, fileParam.getTitle(), fileParam.getSheetName(), Student.class, fileParam.getFileName() + "." + fileParam.getSuffix(), true, response);
                } else if (num < 65000) {
                    // 读数据
                    List<Student> studentList = uploadDownloadService.findAll(uuid);
                    logResult.setCode(0);
                    //不记录返回数据，只做标识
                    logResult.setMsg(fileParam.getSuffix() + " ExportRows:" + num);
                    logResult.setServerID(result.getServerID());
                    logService.logAdd("批次号:" + uuid, "excel导出", "SUCCESS", logResult.toString(), userInfo, request);
                    // 导出
                    FileUtils.exportExcel(studentList, fileParam.getTitle(), fileParam.getSheetName(), Student.class, fileParam.getFileName() + "." + fileParam.getSuffix(), true, response);
                } else {
                    result.setCode(ResultEnum.NODATE.getCode());
                    result.setMsg("导出数据量过大，请联系管理员！");
                    logResult.setCode(0);
                    //不记录返回数据，只做标识
                    logResult.setMsg("导出数据量过大，请联系管理员！");
                    logResult.setServerID(result.getServerID());
                    logService.logAdd("批次号:" + uuid, "excel导出", "SUCCESS", logResult.toString(), userInfo, request);
                }
                return result;
            } else {
                result.setCode(-1);
                result.setMsg("登录超时");
                logService.logAdd("批次号:" + uuid, "excel导出", "ERROR", result.toString(), userInfo, request);
            }
        } catch (Exception e) {
            result.setCode(-1);
            result.setMsg("导出错误");
            logService.logAdd("批次号:" + uuid, "excel导出", "ERROR", result.toString(), userInfo, request);
            result.setCode(ResultEnum.NODATE.getCode());
            result.setMsg("导出错误！");
            return result;
        }
        return result;
    }

    @GetMapping("/templateExcelExportClass")
    @ApiOperation(value = "模板导出_class")
    public void templateExcelExportClass(HttpServletRequest request, HttpServletResponse response) throws IOException {
        // 读取模板excel
        TemplateExportParams params = new TemplateExportParams("src/main/resources/templates/专项支出用款申请书.xls");
        params.setHeadingStartRow(3);
        params.setHeadingRows(2);

        // 不用循环的数据 excel 语法 {{date}}
        Map<String, Object> map = new HashMap<String, Object>();
        map.put("date", "2014-12-25");
        map.put("money", 2000000.00);
        map.put("upperMoney", "贰佰万");
        map.put("company", "执笔潜行科技有限公司");
        map.put("bureau", "财政局");
        map.put("person", "JueYue");
        map.put("phone", "1879740****");

        //List<Map<String, String>> 需要在excel模板中写fe遍历，对象可以自动遍对应单元格位置遍历。
        // fe语法 {{$fe: maplist(需要便利的map名称) t (遍历时的变量)，t.id取值。。。。。 结束}}，某一行总是以{{开始以}}结束，不是每个单元格取值时都加{{}}
        List<TemplateExcelExportEntity> list = new ArrayList<TemplateExcelExportEntity>();

        // 生成测试数据
        for (int i = 0; i < 4; i++) {
            TemplateExcelExportEntity entity = new TemplateExcelExportEntity();
            entity.setIndex(i + 1 + "");
            entity.setAccountType("开源项目");
            entity.setProjectName("EasyPoi " + i + "期");
            entity.setAmountApplied(i * 10000 + "");
            entity.setApprovedAmount((i + 1) * 10000 - 100 + "");
            List<BudgetAccountsEntity> budgetAccounts = Lists.newArrayList();
            for (int j = 0; j < 1; j++) {
                BudgetAccountsEntity accountsEntity = new BudgetAccountsEntity();
                accountsEntity.setCode("A001");
                accountsEntity.setName("设计");
                budgetAccounts.add(accountsEntity);
                accountsEntity = new BudgetAccountsEntity();
                accountsEntity.setCode("A002");
                accountsEntity.setName("开发");
                budgetAccounts.add(accountsEntity);
            }
            entity.setBudgetAccounts(budgetAccounts);
            PayeeEntity payeeEntity = new PayeeEntity();
            payeeEntity.setBankAccount("6222 0000 1234 1234");
            payeeEntity.setBankName("中国银行");
            payeeEntity.setName("小明");
            entity.setPayee(payeeEntity);
            list.add(entity);
        }

        // 导出到模板
        Workbook workbook = ExcelExportUtil.exportExcel(params, TemplateExcelExportEntity.class, list, map);
        File savefile = new File("D:/work/test");
        if (!savefile.exists()) {
            savefile.mkdirs();
        }
        FileOutputStream fos = new FileOutputStream("D:/work/test/专项支出用款申请书.xls");
        workbook.write(fos);
        fos.close();
    }

    @GetMapping("/templateExcelExportMap")
    @ApiOperation(value = "模板导出_map")
    public void templateExcelExportMap(HttpServletRequest request, HttpServletResponse response) throws IOException {
        TemplateExportParams params = new TemplateExportParams(
                "src/main/resources/templates/专项支出用款申请书_map.xls");
        Map<String, Object> map = new HashMap<String, Object>();
        map.put("date", "2014-12-25");
        map.put("money", 2000000.00);
        map.put("upperMoney", "贰佰万");
        map.put("company", "执笔潜行科技有限公司");
        map.put("bureau", "财政局");
        map.put("person", "JueYue");
        map.put("phone", "1879740****");
        List<Map<String, String>> listMap = new ArrayList<Map<String, String>>();
        for (int i = 0; i < 4; i++) {
            Map<String, String> lm = new HashMap<String, String>();
            lm.put("id", i + 1 + "");
            lm.put("zijin", i * 10000 + "");
            lm.put("bianma", "A001");
            lm.put("mingcheng", "设计");
            lm.put("xiangmumingcheng", "EasyPoi " + i + "期");
            lm.put("quancheng", "开源项目");
            lm.put("sqje", i * 10000 + "");
            lm.put("hdje", i * 10000 + "");
            listMap.add(lm);
        }
        // 注意map的key与fe遍历里的名称相同
        map.put("maplist", listMap);
        Workbook workbook = ExcelExportUtil.exportExcel(params, map);
        File savefile = new File("D:/work/test");
        if (!savefile.exists()) {
            savefile.mkdirs();
        }
        FileOutputStream fos = new FileOutputStream("D:/work/test/专项支出用款申请书_map.xls");
        workbook.write(fos);
        fos.close();
    }
}
